use core::{
    cell::UnsafeCell,
    mem::MaybeUninit
};

use crate::{event_flags::EventFlags, kernel::os_kernel_get_tick_count, OS_WAIT_FOREVER};

struct Item<T> {
    value: T,
    tick: u32,
    delay: u32,
}

const FLAG_LOCK: u32 = 0x01;
const FLAG_COND: u32 = 0x02;

unsafe impl<T, const CAPACITY: usize> Send for PriorityQueue<T, CAPACITY> {}
unsafe impl<T, const CAPACITY: usize> Sync for PriorityQueue<T, CAPACITY> {}


pub struct PriorityQueue<T, const CAPACITY: usize> {
    flags: EventFlags,
    data: [UnsafeCell<MaybeUninit<Item<T>>>; CAPACITY],
    size: UnsafeCell<usize>,
}

impl<T, const CAPACITY: usize> PriorityQueue<T, CAPACITY> {
    const ITEM: UnsafeCell<MaybeUninit<Item<T>>> = UnsafeCell::new(MaybeUninit::uninit());
    const DATA: [UnsafeCell<MaybeUninit<Item<T>>>; CAPACITY] = [Self::ITEM; CAPACITY];

    pub const fn const_new() -> Self {
        Self {
            flags: EventFlags::const_new(),
            data: Self::DATA,
            size: UnsafeCell::new(0),
        }
    }

    pub unsafe fn create(&mut self, name: &str) {
        self.flags.create(name);
        self.unlock();
    }

    pub fn post(&self, delay: u32, value: T) -> bool {
        unsafe {
            self.lock();
            let size = *self.size.get();

            if size >= CAPACITY {
                self.unlock();
                return false;
            }

            let item = Item {
                value,
                delay,
                tick: os_kernel_get_tick_count(),
            };
            *self.get(size) = MaybeUninit::new(item);
            *self.size.get() = size + 1;
            self.notify();
            self.unlock();
        }
        true
    }

    pub fn poll(&self) -> T {
        unsafe {
            self.lock();

            loop {
                let current = os_kernel_get_tick_count();
                let mut min_delay = OS_WAIT_FOREVER;

                let size = *self.size.get();

                for i in 0..size {
                    let item = self.get(i);
                    let item2 = item.assume_init_ref();
                    let elapsed = current - item2.tick;

                    if elapsed >= item2.delay {
                        let v = item.assume_init_read();
                        if i != (size - 1) {
                            let end = self.get(size - 1).assume_init_read();
                            *self.get(i) = MaybeUninit::new(end);
                        }
                        *self.size.get() = size - 1;
                        self.unlock();
                        return v.value;
                    }

                    let n = item2.delay - elapsed;
                    if n < min_delay {
                        min_delay = n;
                    }
                }

                self.wait(min_delay);
            }
        }
    }

    #[inline(always)]
    unsafe fn get(&self, index: usize) -> &mut MaybeUninit<Item<T>> {
        &mut *self.data.get_unchecked(index).get()
    }

    unsafe fn wait(&self, timeout: u32) {
        self.unlock();
        _ = self.flags.wait_any_flags(FLAG_COND, timeout);
        self.lock();
    }

    unsafe fn lock(&self) {
        _ = self.flags.wait_any_flags(FLAG_LOCK, OS_WAIT_FOREVER);
    }

    unsafe fn unlock(&self) {
        _ = self.flags.set_flags(FLAG_LOCK);
    }

    unsafe fn notify(&self) {
        _ = self.flags.set_flags(FLAG_COND);
    }
}
